package com.lfy.kcat.user.business.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.lfy.kcat.user.business.DramaBizService;
import com.lfy.kcat.user.cache.CacheData;
import com.lfy.kcat.user.consts.RedisConst;
import com.lfy.kcat.user.feign.ContentServiceFeignClient;
import com.lfy.kcat.user.template.BloomFilterTemplate;
import com.lfy.kcat.user.template.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.dto.home.HomeDramaEpisodesDTO;
import org.dromara.common.core.dto.home.HomeDramaInfoDTO;
import org.dromara.common.core.dto.home.HomeFeaturedDTO;
import org.dromara.common.core.dto.PageReqDTO;
import org.dromara.common.core.enums.FeignEnum;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author leifengyang
 * @version 1.0
 * @date 2025/9/17 10:42
 * @description:
 */
@Slf4j
@Service
public class DramaBizServiceImpl implements DramaBizService {

    @Autowired
    ContentServiceFeignClient contentServiceFeignClient;



    @Autowired
    RedisService redisService;

    @Autowired
    BloomFilterTemplate bfTemplate;

    @Autowired
    RedissonClient redissonClient;


    //模拟本地缓存； 少一次网络交互，更快。
    Map<String, HomeFeaturedDTO> cacheDb = new ConcurrentHashMap<>();



    @Override
    public HomeFeaturedDTO getHomeFeature(PageReqDTO pageReqVo) {
        return contentServiceFeignClient.feature(pageReqVo).getData();
//        //1、从redis中获取首页精选短剧数据； 值就是 HomeFeaturedDTO json数据
//        String cacheKey = RedisConst.HOME_FEATURED_CACHE_KEY +
//            pageReqVo.getPage() + ":" +
//            pageReqVo.getPageSize();
//
//        //2、从缓存中获取
//        HomeFeaturedDTO data = redisService.getData(cacheKey, HomeFeaturedDTO.class);
//        if(data!=null){
//            //3、缓存中有，直接返回
//            return data;
//        }
//
//        //3、说明缓存没有， 需要 回源；
//
//        //4、保存到缓存
//        redisService.saveData(cacheKey, data);
//        return data;
    }

    private HomeFeaturedDTO getDataWithMapCache(PageReqDTO pageReqVo) {
        //1、先看缓存中有没有
        HomeFeaturedDTO data = cacheDb.get("home:featured:1:15");
        //2、如果缓存中有
        if (data != null) {
            log.info("缓存数据命中...");
            return data;
        }

        log.info("缓存数据未命中... 准备回源...");
        //3、缓存中没有，回源获取








        HomeFeaturedDTO dto = contentServiceFeignClient.feature(pageReqVo).getData();
        //4、将数据写入缓存
        log.info("回源完成, 数据写入缓存：");
        cacheDb.put("home:featured:1:15", dto);
        return dto;
    }

    private HomeFeaturedDTO getWithRedisCache(PageReqDTO pageReqVo) {
        //1、从redis中获取首页精选短剧数据； 值就是 HomeFeaturedDTO json数据
//        String cacheKey = "home:featured:1:15";
//        String json = redisTemplate.opsForValue().get(cacheKey);
//        if (StringUtils.isEmpty(json)) {
//            //2、说明缓存没有， 需要 回源；
//            log.info("缓存数据未命中... 准备回源...");
//            HomeFeaturedDTO data = contentServiceFeignClient.feature(pageReqVo).getData();
//            //3、回源数据写入缓存
//            String jsonStr = JSONUtil.toJsonStr(data);
//            log.info("回源完成, 数据写入缓存：{}", jsonStr);
//            redisTemplate.opsForValue().set(cacheKey, jsonStr, 3, TimeUnit.DAYS);
//            return data;
//        }
//
//
//        log.info("缓存命中直接返回....");
//        //4、缓存中有
//        HomeFeaturedDTO dto = JSONUtil.toBean(json, HomeFeaturedDTO.class);
//        return dto;
        return null;
    }


    @CacheData(cacheKey = RedisConst.HOME_DRAMA_EPISODES,
        bloomFilterName = RedisConst.DRAMA_BF)
    public HomeDramaEpisodesDTO getDramaEpisodesInfo(Long dramaId) {
        R<HomeDramaEpisodesDTO> r = contentServiceFeignClient.dramaEpisodesInfo(dramaId);
        HomeDramaEpisodesDTO dbData = r.getData();
        return  dbData;
    }


    /**
     * 完整的缓存逻辑
     * @param dramaId
     * @return
     */
    public HomeDramaEpisodesDTO getDramaEpisodesInfoV1(Long dramaId) {
        //1、查看缓存中有没有
        log.info("缓存中 ==> 获取短剧剧集数据：{}", dramaId);
        HomeDramaEpisodesDTO data = redisService.getData(RedisConst.HOME_DRAMA_EPISODES + dramaId, HomeDramaEpisodesDTO.class);
        if (data != null) {
            log.info("缓存数据命中...，直接返回");
            return data;
        }

        log.info("缓存数据未命中... 准备回源...");
        //2、缓存没有准备回源
        //2.1 先判断BF中是否存在； 防止缓存穿透
        if (!bfTemplate.containsDramaIdBloomFilter(dramaId)) {
            log.info("回源之前：布隆过滤器中不存在，直接返回null：{}", dramaId);
            //2.2 不存在，直接返回
            return null;
        }

        log.info("回源之前：布隆过滤器说有，但缓存没有，准备回源：{}", dramaId);
        //2.2 存在，继续回源; 加分布式锁，防止击穿； lock:dramas:
        RLock lock = redissonClient.getLock(RedisConst.HOME_DRAMA_LOCK + dramaId);
        //2.3 尝试加锁
        log.info("为了防止击穿，所有回源请求，尝试加锁：{}", dramaId);
        try {
            boolean b = lock.tryLock();
            if (b) {
                //加锁成功就回源
                log.info("加锁成功：准备远程获取短剧所有剧集数据：{}", dramaId);
                R<HomeDramaEpisodesDTO> r = contentServiceFeignClient.dramaEpisodesInfo(dramaId);
                HomeDramaEpisodesDTO dbData = r.getData();
                //写入缓存： 缓存null值（防止缓存穿透攻击） + 随机过期时间（防止缓存雪崩）
                //bf 虽然判断了。但是因为误判； 布隆说有，就去回源，数据库有可能没有。
                log.info("加锁成功：数据获取到，保存缓存中：{}", dramaId);
                redisService.saveData(RedisConst.HOME_DRAMA_EPISODES + dramaId,dbData);
//                //2.5 释放锁
//                lock.unlock();
                return dbData;
            }
        }finally {
            //2.6 释放锁
            lock.unlock();
        }


        //2.4 没有拿到锁； 稍等一阵直接获取缓存
        log.info("加锁失败，准备稍等2s去缓存中看一看，之前回源的人会把数据放到缓存：{}", dramaId);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //2.5 最后获取缓存数据库
        data = redisService.getData(RedisConst.HOME_DRAMA_EPISODES + dramaId, HomeDramaEpisodesDTO.class);
        return data;
    }


    @CacheData(cacheKey = RedisConst.HOME_DRAMA_INFO,
    bloomFilterName = RedisConst.DRAMA_BF)
    @Override
    public HomeDramaInfoDTO getDramaInfo(Long dramaId) {
        log.info("准备远程获取短剧信息：{}", dramaId);

        R<HomeDramaInfoDTO> r = contentServiceFeignClient.dramaInfo(dramaId);
        return r.getData();
    }

    @Override
    public void updateDramaDb() {
        //1、更新数据库
        updateDb(1L);
        //2、删缓存
        redisService.deleteCache("drama:" + 1L);
    }

    public void updateDb(Long dramaId) {}
}
